GtkCssNode *selection_node;
GdkContentProvider *provider;
- GList *links;
+ GtkLabelLink *links;
+ guint n_links;
GtkLabelLink *active_link;
GtkLabelLink *context_link;
typedef struct
{
GtkLabel *label;
- GList *links;
+ GArray *links;
GString *new_str;
gsize text_len;
} UriParserData;
if (strcmp (element_name, "a") == 0)
{
- GtkLabelLink *link;
+ GtkLabelLink link;
const gchar *uri = NULL;
const gchar *title = NULL;
const gchar *class = NULL;
visited = FALSE;
if (priv->track_links && priv->select_info)
{
- GList *l;
- for (l = priv->select_info->links; l; l = l->next)
+ for (i = 0; i < priv->select_info->n_links; i++)
{
- link = l->data;
- if (strcmp (uri, link->uri) == 0)
+ const GtkLabelLink *l = &priv->select_info->links[i];
+
+ if (strcmp (uri, l->uri) == 0)
{
- visited = link->visited;
+ visited = l->visited;
break;
}
}
}
- link = g_new0 (GtkLabelLink, 1);
- link->uri = g_strdup (uri);
- link->title = g_strdup (title);
+ if (!pdata->links)
+ pdata->links = g_array_new (FALSE, TRUE, sizeof (GtkLabelLink));
+
+ link.uri = g_strdup (uri);
+ link.title = g_strdup (title);
widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
- link->cssnode = gtk_css_node_new ();
- gtk_css_node_set_name (link->cssnode, g_quark_from_static_string ("link"));
- gtk_css_node_set_parent (link->cssnode, widget_node);
+ link.cssnode = gtk_css_node_new ();
+ gtk_css_node_set_name (link.cssnode, g_quark_from_static_string ("link"));
+ gtk_css_node_set_parent (link.cssnode, widget_node);
if (class)
- gtk_css_node_add_class (link->cssnode, g_quark_from_string (class));
+ gtk_css_node_add_class (link.cssnode, g_quark_from_string (class));
state = gtk_css_node_get_state (widget_node);
if (visited)
state |= GTK_STATE_FLAG_VISITED;
else
state |= GTK_STATE_FLAG_LINK;
- gtk_css_node_set_state (link->cssnode, state);
- g_object_unref (link->cssnode);
+ gtk_css_node_set_state (link.cssnode, state);
+ g_object_unref (link.cssnode);
- link->visited = visited;
- link->start = pdata->text_len;
- pdata->links = g_list_prepend (pdata->links, link);
+ link.visited = visited;
+ link.start = pdata->text_len;
+ g_array_append_val (pdata->links, link);
}
else
{
if (!strcmp (element_name, "a"))
{
- GtkLabelLink *link = pdata->links->data;
+ GtkLabelLink *link = &g_array_index (pdata->links, GtkLabelLink, pdata->links->len - 1);
link->end = pdata->text_len;
}
else
gtk_css_node_set_parent (link->cssnode, NULL);
g_free (link->uri);
g_free (link->title);
- g_free (link);
}
-
static gboolean
-parse_uri_markup (GtkLabel *label,
- const gchar *str,
- gchar **new_str,
- GList **links,
- GError **error)
+parse_uri_markup (GtkLabel *label,
+ const gchar *str,
+ gchar **new_str,
+ GtkLabelLink **links,
+ guint *out_n_links,
+ GError **error)
{
GMarkupParseContext *context = NULL;
const gchar *p, *end;
g_markup_parse_context_free (context);
*new_str = g_string_free (pdata.new_str, FALSE);
- *links = pdata.links;
+
+ if (pdata.links)
+ {
+ *out_n_links = pdata.links->len;
+ *links = (GtkLabelLink *)g_array_free (pdata.links, FALSE);
+ }
+ else
+ {
+ *links = NULL;
+ }
return TRUE;
failed:
g_markup_parse_context_free (context);
g_string_free (pdata.new_str, TRUE);
- g_list_free_full (pdata.links, (GDestroyNotify) link_free);
+
+ if (pdata.links)
+ g_array_free (pdata.links, TRUE);
return FALSE;
}
gtk_label_ensure_has_tooltip (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- GList *l;
+ guint i;
gboolean has_tooltip = FALSE;
- for (l = priv->select_info->links; l; l = l->next)
+ for (i = 0; i < priv->select_info->n_links; i++)
{
- GtkLabelLink *link = l->data;
+ const GtkLabelLink *link = &priv->select_info->links[i];
+
if (link->title)
{
has_tooltip = TRUE;
gunichar accel_char = 0;
gchar *str_for_display = NULL;
gchar *str_for_accel = NULL;
- GList *links = NULL;
+ GtkLabelLink *links = NULL;
+ guint n_links = 0;
- if (!parse_uri_markup (label, str, &str_for_display, &links, &error))
+ if (!parse_uri_markup (label, str, &str_for_display, &links, &n_links, &error))
{
g_warning ("Failed to set text '%s' from markup due to error parsing markup: %s",
str, error->message);
if (links)
{
gtk_label_ensure_select_info (label);
- priv->select_info->links = g_list_reverse (links);
+ priv->select_info->links = g_steal_pointer (&links);
+ priv->select_info->n_links = n_links;
_gtk_label_accessible_update_links (label);
gtk_label_ensure_has_tooltip (label);
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (label)), "link");
if (priv->layout == NULL)
return;
-
if (priv->select_info && priv->select_info->links)
{
const GdkRGBA *link_color;
PangoAttribute *attribute;
- GList *list;
+ guint i;
attrs = pango_attr_list_new ();
- for (list = priv->select_info->links; list; list = list->next)
+ for (i = 0; i < priv->select_info->n_links; i++)
{
- GtkLabelLink *link = list->data;
+ const GtkLabelLink *link = &priv->select_info->links[i];
attribute = pango_attr_underline_new (TRUE);
attribute->start_index = link->start;
update_link_state (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- GList *l;
GtkStateFlags state;
+ guint i;
if (!priv->select_info)
return;
- for (l = priv->select_info->links; l; l = l->next)
+ for (i = 0; i < priv->select_info->n_links; i++)
{
- GtkLabelLink *link = l->data;
+ const GtkLabelLink *link = &priv->select_info->links[i];
state = gtk_widget_get_state_flags (GTK_WIDGET (label));
if (link->visited)
}
static GtkLabelLink *
-gtk_label_get_focus_link (GtkLabel *label)
+gtk_label_get_focus_link (GtkLabel *label,
+ int *out_index)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
- GList *l;
+ int link_index;
- if (!info)
- return NULL;
+ if (!info ||
+ info->selection_anchor != info->selection_end)
+ goto nope;
- if (info->selection_anchor != info->selection_end)
- return NULL;
+ link_index = _gtk_label_get_link_at (label, info->selection_anchor);
- for (l = info->links; l; l = l->next)
+ if (link_index != -1)
{
- GtkLabelLink *link = l->data;
- if (link->start <= info->selection_anchor &&
- info->selection_anchor <= link->end)
- return link;
+ if (out_index)
+ *out_index = link_index;
+
+ return &info->links[link_index];
}
+nope:
+ if (out_index)
+ *out_index = -1;
return NULL;
}
cursor_direction);
}
- focus_link = gtk_label_get_focus_link (label);
+ focus_link = gtk_label_get_focus_link (label, NULL);
active_link = info->active_link;
if (active_link)
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
gboolean select_on_focus;
- GtkLabelLink *link;
- GList *l;
if (priv->select_info == NULL)
return FALSE;
{
if (priv->select_info->links && !priv->in_click)
{
- for (l = priv->select_info->links; l; l = l->next)
+ guint i;
+
+ for (i = 0; i < priv->select_info->n_links; i++)
{
- link = l->data;
+ const GtkLabelLink *link = &priv->select_info->links[i];
+
if (!range_is_in_ellipsis (label, link->start, link->end))
{
priv->select_info->selection_anchor = link->start;
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
GtkLabelLink *focus_link;
- GList *l;
if (!gtk_widget_is_focus (widget))
{
gtk_widget_grab_focus (widget);
if (info)
{
- focus_link = gtk_label_get_focus_link (label);
+ focus_link = gtk_label_get_focus_link (label, NULL);
if (focus_link && direction == GTK_DIR_TAB_BACKWARD)
{
- for (l = g_list_last (info->links); l; l = l->prev)
+ int i;
+ for (i = info->n_links - 1; i >= 0; i--)
{
- focus_link = l->data;
+ focus_link = &info->links[i];
if (!range_is_in_ellipsis (label, focus_link->start, focus_link->end))
{
info->selection_anchor = focus_link->start;
index = info->selection_anchor;
if (direction == GTK_DIR_TAB_FORWARD)
- for (l = info->links; l; l = l->next)
- {
- GtkLabelLink *link = l->data;
+ {
+ guint i;
+ for (i = 0; i < info->n_links; i++)
+ {
+ const GtkLabelLink *link = &info->links[i];
- if (link->start > index)
- {
- if (!range_is_in_ellipsis (label, link->start, link->end))
- {
- gtk_label_select_region_index (label, link->start, link->start);
- _gtk_label_accessible_focus_link_changed (label);
- return TRUE;
- }
- }
- }
+ if (link->start > index)
+ {
+ if (!range_is_in_ellipsis (label, link->start, link->end))
+ {
+ gtk_label_select_region_index (label, link->start, link->start);
+ _gtk_label_accessible_focus_link_changed (label);
+ return TRUE;
+ }
+ }
+ }
+ }
else if (direction == GTK_DIR_TAB_BACKWARD)
- for (l = g_list_last (info->links); l; l = l->prev)
- {
- GtkLabelLink *link = l->data;
+ {
+ int i;
+ for (i = info->n_links - 1; i >= 0; i--)
+ {
+ GtkLabelLink *link = &info->links[i];
- if (link->end < index)
- {
- if (!range_is_in_ellipsis (label, link->start, link->end))
- {
- gtk_label_select_region_index (label, link->start, link->start);
- _gtk_label_accessible_focus_link_changed (label);
- return TRUE;
- }
- }
- }
+ if (link->end < index)
+ {
+ if (!range_is_in_ellipsis (label, link->start, link->end))
+ {
+ gtk_label_select_region_index (label, link->start, link->start);
+ _gtk_label_accessible_focus_link_changed (label);
+ return TRUE;
+ }
+ }
+ }
+ }
goto out;
}
else
{
- focus_link = gtk_label_get_focus_link (label);
+ int focus_link_index;
+ int new_index = -1;
+ int i;
+
+ focus_link = gtk_label_get_focus_link (label, &focus_link_index);
+
switch (direction)
{
case GTK_DIR_TAB_FORWARD:
if (focus_link)
- {
- l = g_list_find (info->links, focus_link);
- l = l->next;
- }
+ new_index = (focus_link_index + 1) % info->n_links;
else
- l = info->links;
- for (; l; l = l->next)
+ new_index = 0;
+
+ for (i = new_index; i < info->n_links; i++)
{
- GtkLabelLink *link = l->data;
+ const GtkLabelLink *link = &info->links[i];
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
case GTK_DIR_TAB_BACKWARD:
if (focus_link)
- {
- l = g_list_find (info->links, focus_link);
- l = l->prev;
- }
+ new_index = focus_link_index == 0 ? info->n_links - 1 : focus_link_index - 1;
else
- l = g_list_last (info->links);
- for (; l; l = l->prev)
+ new_index = info->n_links - 1;
+
+ for (i = new_index; i >= 0; i--)
{
- GtkLabelLink *link = l->data;
+ const GtkLabelLink *link = &info->links[i];
if (!range_is_in_ellipsis (label, link->start, link->end))
break;
}
goto out;
}
- if (l)
+ if (new_index != -1)
{
- focus_link = l->data;
+ focus_link = &info->links[new_index];
info->selection_anchor = focus_link->start;
info->selection_end = focus_link->start;
_gtk_label_accessible_focus_link_changed (label);
if (info->links && !info->in_drag)
{
- GList *l;
GtkLabelLink *link;
gboolean found = FALSE;
{
if (get_layout_index (label, x, y, &index))
{
- for (l = info->links; l != NULL; l = l->next)
+ const int link_index = _gtk_label_get_link_at (label, index);
+
+ if (link_index != -1)
{
- link = l->data;
- if (index >= link->start && index <= link->end)
- {
- if (!range_is_in_ellipsis (label, link->start, link->end))
- found = TRUE;
- break;
- }
+ link = &info->links[link_index];
+
+ if (!range_is_in_ellipsis (label, link->start, link->end))
+ found = TRUE;
}
}
}
{
GtkLabelLink *link;
- link = gtk_label_get_focus_link (label);
+ link = gtk_label_get_focus_link (label, NULL);
if (link)
gdk_clipboard_set_text (clipboard, link->uri);
}
{
GtkLabel *label = GTK_LABEL (widget);
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- GtkLabelLink *link = priv->select_info->context_link;
+ GtkLabelLink *link = priv->select_info->context_link;
if (link)
emit_activate_link (label, link);
else
{
has_selection = FALSE;
- link = gtk_label_get_focus_link (label);
+ link = gtk_label_get_focus_link (label, NULL);
}
gtk_widget_action_set_enabled (widget, "clipboard.copy", has_selection);
if (priv->select_info->link_clicked)
priv->select_info->context_link = priv->select_info->active_link;
else
- priv->select_info->context_link = gtk_label_get_focus_link (label);
+ priv->select_info->context_link = gtk_label_get_focus_link (label, NULL);
gtk_label_update_actions (label);
gtk_label_clear_links (GtkLabel *label)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
+ guint i;
if (!priv->select_info)
return;
- g_list_free_full (priv->select_info->links, (GDestroyNotify) link_free);
+ for (i = 0; i < priv->select_info->n_links; i++)
+ link_free (&priv->select_info->links[i]);
+ g_free (priv->select_info->links);
priv->select_info->links = NULL;
+ priv->select_info->n_links = 0;
priv->select_info->active_link = NULL;
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (label)), "link");
GtkLabelLink *link;
GtkWidget *widget = GTK_WIDGET (label);
- link = gtk_label_get_focus_link (label);
+ link = gtk_label_get_focus_link (label, NULL);
if (link)
emit_activate_link (label, link);
if (priv->select_info->link_clicked)
link = priv->select_info->active_link;
else
- link = gtk_label_get_focus_link (label);
+ link = gtk_label_get_focus_link (label, NULL);
return link;
}
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
GtkLabelSelectionInfo *info = priv->select_info;
gint index = -1;
- GList *l;
if (info && info->links)
{
if (index != -1)
{
- for (l = info->links; l != NULL; l = l->next)
+ const int link_index = _gtk_label_get_link_at (label, index);
+
+ if (link_index != -1)
{
- GtkLabelLink *link = l->data;
- if (index >= link->start && index <= link->end)
+ const GtkLabelLink *link = &info->links[link_index];
+
+ if (link->title)
{
- if (link->title)
- {
- gtk_tooltip_set_markup (tooltip, link->title);
- return TRUE;
- }
- break;
+ gtk_tooltip_set_markup (tooltip, link->title);
}
}
}
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
if (priv->select_info)
- return g_list_length (priv->select_info->links);
+ return priv->select_info->n_links;
return 0;
}
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
if (priv->select_info)
- {
- GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
- if (link)
- return link->uri;
- }
+ return priv->select_info->links[idx].uri;
return NULL;
}
gint *end)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- gint i;
- GList *l;
- GtkLabelLink *link;
if (priv->select_info)
- for (l = priv->select_info->links, i = 0; l; l = l->next, i++)
- {
- if (i == idx)
- {
- link = l->data;
- *start = link->start;
- *end = link->end;
- return;
- }
- }
+ {
+ const GtkLabelLink *link = &priv->select_info->links[idx];
- *start = -1;
- *end = -1;
+ *start = link->start;
+ *end = link->end;
+ }
+ else
+ {
+ *start = -1;
+ *end = -1;
+ }
}
-gint
-_gtk_label_get_link_at (GtkLabel *label,
- gint pos)
+int
+_gtk_label_get_link_at (GtkLabel *label,
+ int pos)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- gint i;
- GList *l;
- GtkLabelLink *link;
if (priv->select_info)
- for (l = priv->select_info->links, i = 0; l; l = l->next, i++)
- {
- link = l->data;
- if (link->start <= pos && pos < link->end)
- return i;
- }
+ {
+ guint i;
+
+ for (i = 0; i < priv->select_info->n_links; i++)
+ {
+ const GtkLabelLink *link = &priv->select_info->links[i];
+
+ if (link->start <= pos && pos < link->end)
+ return i;
+ }
+ }
return -1;
}
if (priv->select_info)
{
- GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
+ GtkLabelLink *link = &priv->select_info->links[idx];
- if (link)
- emit_activate_link (label, link);
+ emit_activate_link (label, link);
}
}
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
if (priv->select_info)
- {
- GtkLabelLink *link = g_list_nth_data (priv->select_info->links, idx);
- return link ? link->visited : FALSE;
- }
+ return priv->select_info->links[idx].visited;
return FALSE;
}
gint idx)
{
GtkLabelPrivate *priv = gtk_label_get_instance_private (label);
- gint i;
- GList *l;
- GtkLabelLink *link;
GtkLabelSelectionInfo *info = priv->select_info;
if (!info)
if (info->selection_anchor != info->selection_end)
return FALSE;
- for (l = info->links, i = 0; l; l = l->next, i++)
+ if (idx >= 0 && idx < info->n_links)
{
- if (i == idx)
- {
- link = l->data;
- if (link->start <= info->selection_anchor &&
- info->selection_anchor <= link->end)
- return TRUE;
- }
+ const GtkLabelLink *link = &info->links[idx];
+
+ if (link->start <= info->selection_anchor &&
+ info->selection_anchor <= link->end)
+ return TRUE;
}
return FALSE;